module ysyx_23060189_IFU #(parameter xlen = 32) (
  input                  clk,
  input                  rst,
  output reg             done,

  /* data: WBU <=> IFU */
  input  wire [1:0]      wb_PC_sel,
  input  wire [1:0]      wb_wb_sel,
  input  wire            wb_wb_en,
  input  wire            wb_br_taken,
  input  wire [xlen-1:0] wb_csr_out,
  input  wire [xlen-1:0] wb_Alu_out,
  input  wire [4:0]      wb_wb_addr,
  input  wire [xlen-1:0] wb_rd_data,

  input  wire            wb_valid,
  output wire            if_ready,

  /* data: IFU <=> IDU */
  output wire [xlen-1:0] if_inst,
  output wire [xlen-1:0] if_pc,
  output wire [4:0]      if_wb_addr,
  output wire [xlen-1:0] if_rs1_data,
  output wire [xlen-1:0] if_rs2_data,

  output wire            if_valid,
  input  wire            de_ready,

  /* IFU <=> Master interface */
  input  wire            ACLK,
  input  wire            ARESETn,
  // read
  output reg             ren,
  output wire [xlen-1:0] raddr,
  output wire [2:0]      arsize,
  input  wire [xlen-1:0] rdata,
  input  wire            rvalid,

  // write
  output wire            wen,
  output wire [xlen-1:0] waddr,
  output wire [xlen-1:0] wdata,
  output wire [7:0]      wmask,
  output wire [2:0]      awsize,
  input  wire            wdone
);

  localparam size = xlen == 32 ? 3'b010 : xlen == 64 ? 3'b011 : xlen / 8;
  // reg define
  reg ARESETn_cur;
  reg ARESETn_pre;
  reg [xlen-1:0] inst;
  reg inst_valid;

  // wire define
  wire [xlen-1:0] pc_4;
  wire [4:0]      rs1_addr;
  wire [4:0]      rs2_addr;
  wire            ARESETn_raise;

  assign raddr = if_pc;
  assign arsize = size[2:0];

  // do not need to write
  assign wen    = 0;
  assign waddr  = 0;
  assign wdata  = 0;
  assign wmask  = 0;
  assign awsize = size[2:0];

  // 指令有效，则if_valid=1 否则为0
  assign if_valid = inst_valid;
  assign if_ready = 1;

  assign if_inst = inst;

  assign rs1_addr = inst[19:15];
  assign rs2_addr = inst[24:20];
  assign if_wb_addr = inst[11:7];

  ysyx_23060189_PC Pc(
    .clk(clk),
    .rst(rst),
    .PC_sel(wb_PC_sel),
    .Alu_out(wb_Alu_out),
    .br_taken(wb_br_taken),
    .csr_out(wb_csr_out),
    .wen(inst_valid & wb_valid),
    .pc_out(if_pc),
    .pc_4(pc_4)
  );

  ysyx_23060189_RegisterFile RegisterFile(
    .clk(clk),
    .wb_en(wb_wb_en & wb_valid),
    .Alu_out(wb_Alu_out),
    .csr_out(wb_csr_out),
    .pc_4(pc_4),
    .wb_sel(wb_wb_sel),
    .wb_addr(wb_wb_addr),
    .rs1_addr(rs1_addr),
    .rs2_addr(rs2_addr),
    .rd_data(wb_rd_data),
    .rs1_data(if_rs1_data),
    .rs2_data(if_rs2_data)
  );

  // get raising edge of ARESETn 
  assign ARESETn_raise = (!ARESETn_pre) && ARESETn_cur;
  always @(posedge ACLK) begin
    if (ARESETn == 0) begin
      ARESETn_cur <= 0;
      ARESETn_pre <= 0;
    end
    else begin
      ARESETn_cur <= ARESETn;
      ARESETn_pre <= ARESETn_cur;
    end
  end
  // enable to read instruction
  always @(posedge ACLK) begin
    if (ARESETn == 0) ren <= 0;
    else if (ARESETn_raise == 1) ren <= 1;
    else if (inst_valid && wb_valid) ren <= 1;
    else ren <= 0;
  end
  // read instruction
  always @(posedge ACLK) begin
    if (ARESETn == 0) inst <= 0;
    else if (rvalid) inst <= rdata;
    else inst <= inst;
  end
  // inst_valid
  always @(posedge ACLK) begin
    if (ARESETn == 0) inst_valid <= 1'b0;
    else if (!inst_valid && rvalid) inst_valid <= 1'b1;
    else if (inst_valid && wb_valid) inst_valid <= 1'b0;
    else inst_valid <= inst_valid;
  end

  // end of instruction execution
  always @(posedge clk) begin
    if (rst) done <= 1'b0;
    else if (inst_valid && wb_valid) done <= 1'b1;
    else done <= 1'b0;
  end

endmodule
